﻿/*
 * imaeg - generic image utility in C#
 * Copyright (C) 2010  ed <tripflag@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License v2
 * (version 2) as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, refer to the following URL:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using System.Drawing.Imaging;

namespace imaeg {
    class ImgMan {
        const int MAXQUE = 5;

        string root;
        List<Img> image;
        List<Img> queue;
        public ImgFilter filter;
        public ImgMan(string root, string[] names) {
            //debug();

            // Alternative code to generate "LOL LOADAN BORKED" image
            // Looks neat, but way too load intensive.
            // In other words: Plain fucking bloat.
            
            //Size sz = new Size(480, 270);
            //failed = new Bitmap(sz.Width, sz.Height);
            /*using (Graphics g = Graphics.FromImage(failed)) {
                /*g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
                g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;* /
                g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;

                string[] str = { "- ERROR -", "BAD_FILE" };
                Font fn = new Font("Verdana", 64, FontStyle.Bold);
                int span = sz.Height / str.Length;
                for (int a = 0; a < str.Length; a++) {
                    drawText(sz, g, str[a],
                        "Verdana", 48, FontStyle.Bold,
                        Brushes.White, Brushes.Gray,
                        new PointF(
                            sz.Width / 2,
                            a * span + span / 2));
                }
            }*/
            /*using (Graphics g = Graphics.FromImage(failed)) {
                string[] str = { "- ERROR -", "BAD_FILE" };
                Font fn = new Font("Verdana", 56, FontStyle.Bold);
                g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                int span = sz.Height / str.Length;
                SizeF[] fsz = new SizeF[str.Length];
                for (int a = 0; a < str.Length; a++) {
                    fsz[a] = g.MeasureString(str[a], fn);
                    PointF pt = new PointF(
                        (sz.Width - fsz[a].Width) / 2,
                        a * span + (span - fsz[a].Height) / 2);
                    g.DrawString(str[a], fn, Brushes.Black, pt.X + 1.5f, pt.Y + 1.5f);
                    //g.DrawString(str[a], fn, Brushes.White, pt.X - 1.5f, pt.Y - 1.5f);
                    g.DrawString(str[a], fn, Brushes.Silver, pt.X + 0, pt.Y + 0);
                }
            }*/

            this.root = root;
            image = new List<Img>();
            queue = new List<Img>();
            foreach (string name in names)
                image.Add(new Img(name));

            filter = new ImgFilter();
            Thread loada = new Thread(new ThreadStart(loader));
            loada.Priority = ThreadPriority.BelowNormal;
            loada.IsBackground = true;
            loada.Start();

            Program.dbg("ImgMan instance created");
        }

        Image bad() {
            Size sz = Screen.AllScreens[0].Bounds.Size;
            //sz = new Size(480,   270);  270p  64e 4.22m ~4.2
            //sz = new Size(800,   450);  450p 107e 4.21m ~4.2
            //sz = new Size(1024,  576);  576p 137e 4.20m ~4.2
            //sz = new Size(1280,  720);  720p 172e 4.18m ~4.2
            //sz = new Size(1920, 1080); 1080p 184e 5.87m wat?
            //sz = new Size(2560, 1600); 1600p   ?e    ?m fuck
            Bitmap ret = new Bitmap(sz.Width, sz.Height);
            using (Graphics g = Graphics.FromImage(ret)) {
                string[] str = { "BAD  FILE", "DESU  YO" };
                Font fn = new Font("Verdana", ret.Height / 5, FontStyle.Bold);
                g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                int span = sz.Height / str.Length;
                SizeF[] fsz = new SizeF[str.Length];
                for (int a = 0; a < str.Length; a++) {
                    fsz[a] = g.MeasureString(str[a], fn);
                    PointF pt = new PointF(
                        (sz.Width - fsz[a].Width) / 2,
                        a * span + (span - fsz[a].Height) / 2);
                    g.DrawString(str[a], fn, Brushes.White, pt.X + 3, pt.Y + 1);
                    g.DrawString(str[a], fn, Brushes.Black, pt.X + 1, pt.Y + 3);
                    g.DrawString(str[a], fn, Brushes.Gray, pt.X, pt.Y);
                }
            }
            Program.dbg("Substitute image created");
            return ret;
        }

        // Helper methods for the alternative BADIMAGE renderer.
        // Slow but nice.

        /*void drawText(Size bsz, Graphics g, string str, string fN, int fE, FontStyle fS, Brush fg, Brush bg, PointF center) {
            float cnt = 20, stp = 1f;
            for (float a = 1; a < cnt; a += stp) {
                using (Font fn = new Font(fN, fE + (cnt - a), fS)) {
                    SizeF sz = g.MeasureString(str, fn);
                    PointF pt = new PointF(
                        center.X - sz.Width / 2,
                        center.Y - sz.Height / 2);
                    using (Bitmap bm = new Bitmap(bsz.Width, bsz.Height)) {
                        using (Graphics gg = Graphics.FromImage(bm)) {
                            gg.DrawString(str, fn, bg, pt);
                        }
                        drawAlpha(g, bm, a / 20f);
                    }
                }
            }
            using (Font fn = new Font(fN, fE, fS)) {
                SizeF sz = g.MeasureString(str, fn);
                PointF pt = new PointF(
                    center.X - sz.Width / 2,
                    center.Y - sz.Height / 2);
                g.DrawString(str, fn, fg, pt);
            }
        }
        void drawAlpha(Graphics g, Image im, float a) {
            ImageAttributes derp = new ImageAttributes();
            derp.SetColorMatrix(new ColorMatrix(new float[][]{
                            new float[]{a, 0, 0, 0, 0},
                            new float[]{0, a, 0, 0, 0},
                            new float[]{0, 0, a, 0, 0},
                            new float[]{0, 0, 0, a/8, 0},
                            new float[]{0, 0, 0, 0, 0}}));
            //g.DrawImage(im, Point.Empty, new Rectangle(Point.Empty, im.Size), GraphicsUnit.Pixel, derp);
            g.DrawImage(im, new Rectangle(Point.Empty, im.Size), 0, 0,
                im.Width, im.Height, GraphicsUnit.Pixel, derp);
        }*/

        Label lb;
        void debug() {
            lb = new Label();
            lb.Visible = true;
            lb.Dock = DockStyle.Fill;
            Form fm = new Form();
            fm.Controls.Add(lb);
            fm.Size = new Size(320, 900);
            fm.TopMost = true;
            fm.Show();
            System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
            t.Tick += new EventHandler(t_Tick);
            t.Interval = 100;
            t.Start();
        }
        void t_Tick(object sender, EventArgs e) {
            StringBuilder a = new StringBuilder();
            StringBuilder b = new StringBuilder();
            for (int i = 0; i < image.Count; i++) {
                if (image[i].finished) {
                    a.AppendLine(i + " - " + image[i].getName());
                }
            }
            for (int i = 0; i < queue.Count; i++) {
                b.AppendLine(i + " - " + queue[i].getName());
            }
            lb.Text = System.DateTime.Now.Ticks +
                "\n\n" + a.ToString() +
                "\n\n" + b.ToString();
        }

        /// <summary>
        /// Returns image i, applying all configured enhancements
        /// </summary>
        /// <param name="i">The image to return</param>
        /// <returns>Image i</returns>
        public Image get(int i) {

            // Start off with unloading excessive images
            for (int a = 0; a < i - 3; a++) {
                image[a].dispose();
            }
            for (int a = i + 3; a < image.Count; a++) {
                image[a].dispose();
            }

            // New filter settings?
            if (filter.hasChanged()) { //ALTERNATIVE_CODE
                foreach (Img im in image) {
                    im.dispose();
                }
            }

            queue.Clear();
            queue.Add(image[i]);
            enqueue(i + 1);
            enqueue(i + 2);
            enqueue(i - 1);
            enqueue(i - 2);

            while (!image[i].finished)
                Thread.Sleep(1);
            return image[i].failed ? bad() :
                //(Image)failed.Clone() :
                (Image)image[i].get().Clone();
        }

        /// <summary>
        /// Enqueue image i at a lower priority (caching)
        /// </summary>
        /// <param name="i"></param>
        void enqueue(int i) {
            if (i < 0 || i >= image.Count) return;
            if (!image[i].finished) {
                //foreach (Img j in queue)
                    //if (image[i] == j) return;
                queue.Add(image[i]);
                //queue.Insert(1, image[i]);
            }
        }

        /// <summary>
        /// Worker thread; loads images.
        /// </summary>
        void loader() {
            while (true) {
                if (queue.Count > 0) {
                    Img i = queue[0];
                    queue.RemoveAt(0);
                    Program.dbg("Loader requested on " + i.getName());
                    if (!i.finished) {
                        //i.load(root);
                        Program.dbg("Loader executing on " + i.getName());
                        i.load(root, filter); //ALTERNATIVE_CODE
                        //i.set(filter.apply(i.get())); //ALTERNATIVE_CODE
                    }
                } else {
                    Thread.Sleep(10);
                }
            }
        }

        public int length {
            get { return image.Count; }
            set { }
        }
        public string getName(int i) {
            return image[i].getName();
        }
        public void setName(int i, string s) {
            image[i].setName(s);
        }
        public void remove(int i) {
            image.RemoveAt(i);
        }
    }
}
